/* ************************************************************************** *
 * See the NOTICE file distributed with this work for additional information
 * regarding copyright ownership.
 * 
 * This file is licensed to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 * ************************************************************************** */
package org.statewalker.fsm;

/**
 * A simple state listener used to trace all transitions in a form of an XML.
 * This class opens a new XML tag when the process enters in a new state and
 * closes it when it leaves this state.
 * 
 * @author kotelnikov
 * @param <S>
 * @param <E>
 */
public class PrintStateListener<S, E> implements IFsmStateListener<S, E> {

    /**
     * The internal field used to keep the depths of the stack.
     */
    protected int fDepth;

    /**
     * If this flag is <code>true</code> then this printer writes each new tag
     * (opening or closing one) starting from a new line with the identation
     * corresponding to the tag depth.
     */
    protected boolean fIdent;

    /**
     * If this flag is <code>true</code> then the events initializing
     * transitions are also written in the resulting XML.
     */
    protected boolean fPrintEvents;

    /**
     * If this flag is <code>true</code> then this class writes opening/closing
     * tags for each state.
     */
    protected boolean fPrintStates;

    /**
     * If this flag is <code>true</code> then this class dumps transitions.
     */
    protected boolean fPrintTransitions;

    /**
     * The default constructor.
     */
    public PrintStateListener() {
        this(true, true, true);
    }

    /**
     * This constructor defines if this printer should write states, transitions
     * and identations for all states and transitions.
     * 
     * @param printStates
     * @param printTransitions
     * @param ident
     */
    public PrintStateListener(
        boolean printStates,
        boolean printTransitions,
        boolean ident) {
        fPrintStates = printStates;
        fPrintTransitions = printTransitions;
        fIdent = ident;
    }

    /**
     * This method returns a string representation of the given event.
     * 
     * @param event the event to serialze as a string
     * @return the name corresponding to the given event.
     */
    protected String getEventName(E event) {
        return event.toString();
    }

    /**
     * This method returns a tag name corresponding to the given state.
     * 
     * @param state the state to serialize as a string
     * @return the name of the tag corresponding to the given state
     */
    protected String getStateName(S state) {
        return state != null ? state.toString() : null;
    }

    /**
     * @see org.statewalker.fsm.IFsmStateListener#onActivate(java.lang.Object,
     *      java.lang.Object, java.lang.Object)
     */
    public void onActivate(S parent, S state, E event) {
        if (fPrintStates) {
            String stateName = getStateName(state);
            String eventName = getEventName(event);
            println("<"
                + stateName
                + (fPrintEvents ? " event='" + eventName + "'" : "")
                + ">");
        }
        fDepth++;
    }

    /**
     * @see org.statewalker.fsm.IFsmStateListener#onDeactivate(java.lang.Object,
     *      java.lang.Object, java.lang.Object)
     */
    public void onDeactivate(S parent, S state, E event) {
        fDepth--;
        if (fPrintStates) {
            String stateName = getStateName(state);
            String eventName = getEventName(event);
            println("</"
                + stateName
                + ">"
                + (fPrintEvents ? "<!-- Event: " + eventName + " -->" : ""));
        }
    }

    /**
     * @see org.statewalker.fsm.IFsmStateListener#onTransition(java.lang.Object,
     *      java.lang.Object, java.lang.Object, java.lang.Object)
     */
    public void onTransition(S parent, S prev, E event, S next) {
        if (fPrintTransitions) {
            String stateName = getStateName(parent);
            String fromName = getStateName(prev);
            String toName = getStateName(next);
            String eventName = getEventName(event);
            println("<transition parent='"
                + stateName
                + "' from='"
                + fromName
                + "' event='"
                + eventName
                + "' to='"
                + toName
                + "' "
                + "/>");
        }

    }

    /**
     * Writes the given string. By default this method just prints the given
     * string in the standard output. This method should be overloaded to define
     * real operations.
     * 
     * @param str the string write
     */
    public void print(String str) {
        System.out.print(str);
    }

    /**
     * Prints the given string and ends the line with the EOL symbol.
     * 
     * @param str the string to write
     */
    public void println(String str) {
        if (fIdent) {
            for (int i = 0; i < fDepth; i++)
                print("    ");
        }
        print(str);
        if (fIdent) {
            print("\n");
        }
    }

}